package cn.madis.ymcc.service.impl;

import cn.madis.exception.GlobalException;
import cn.madis.ymcc.CourseFeignClient;
import cn.madis.ymcc.domain.Course;
import cn.madis.ymcc.domain.CourseOrder;
import cn.madis.ymcc.domain.CourseOrderItem;
import cn.madis.ymcc.domain.dto.Order2PayDto;
import cn.madis.ymcc.dto.ForecastOrder;
import cn.madis.ymcc.dto.PayNotifyDto;
import cn.madis.ymcc.mapper.CourseOrderMapper;
import cn.madis.ymcc.result.JSONResult;
import cn.madis.ymcc.service.ICourseOrderItemService;
import cn.madis.ymcc.service.ICourseOrderService;
import cn.madis.ymcc.util.AssertUtil;
import cn.madis.ymcc.util.CodeGenerateUtils;
import cn.madis.ymcc.utils.LoginSecurityContext;
import cn.madis.ymcc.vo.Course2OrderMax;
import cn.madis.ymcc.vo.Course2OrderMin;
import cn.madis.ymcc.vo.CoursePlaceOrderVo;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.mapper.EntityWrapper;
import com.baomidou.mybatisplus.service.impl.ServiceImpl;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.rocketmq.client.producer.LocalTransactionState;
import org.apache.rocketmq.client.producer.SendResult;
import org.apache.rocketmq.client.producer.SendStatus;
import org.apache.rocketmq.client.producer.TransactionSendResult;
import org.apache.rocketmq.spring.core.RocketMQTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author Zt
 * @since 2022-08-18
 */
@Service
@Slf4j
public class CourseOrderServiceImpl extends ServiceImpl<CourseOrderMapper, CourseOrder> implements ICourseOrderService {
    @Autowired
    private RedisTemplate<Object,Object> redisTemplate;

    @Autowired
    private CourseFeignClient courseFeignClient;

    @Autowired
    private ICourseOrderService courseOrderService;

    @Autowired
    private ICourseOrderItemService courseOrderItemService;

    @Autowired
    private RocketMQTemplate rocketMQTemplate;
    /**
     * 下单
     * @param coursePlaceOrderVo
     */
    @Override
    public String placeOrder(CoursePlaceOrderVo coursePlaceOrderVo) {
        //1.校验参数jsr303
        //2.防止重复token校验
        Long loginId = LoginSecurityContext.getLoginJwtDtoContext().getId();
        List<Long> courseIds = coursePlaceOrderVo.getCourseIds();
        String courseId = StringUtils.join(courseIds, ",");
        Object token = redisTemplate.opsForValue().get(loginId + ":" + courseId);
        AssertUtil.isNotNull(token,"请勿重复下单");
        boolean equals = coursePlaceOrderVo.getToken().equals(token.toString());
        AssertUtil.isTrue(equals,"token不正确");
        //3.下单逻辑 用feign去调用course查询到corse和courseMarket数据
        JSONResult info = courseFeignClient.info(courseId);
        AssertUtil.isTrue(info.isSuccess(),info.getMessage());
        Object data = info.getData();
        AssertUtil.isNotNull(data,"没有课程");
        Course2OrderMax course2OrderMax = JSONObject.parseObject(JSON.toJSONString(data), Course2OrderMax.class);
        BigDecimal totalAmount = course2OrderMax.getTotalAmount();
        List<Course2OrderMin> courseInfos = course2OrderMax.getCourseInfos();

        //==========================================
        Date date = new Date();
        //保存t_course_order表
        CourseOrder courseOrder = new CourseOrder();
        courseOrder.setCreateTime(date);//创建时间
        courseOrder.setOrderNo(CodeGenerateUtils.generateOrderSn(loginId));//订单编号
        courseOrder.setTotalAmount(course2OrderMax.getTotalAmount());//支付总的价格
        courseOrder.setStatusOrder(CourseOrder.STATE_UNPAID);//订单状态
        courseOrder.setUserId(loginId);//用户
        courseOrder.setPayType(coursePlaceOrderVo.getPayType());//支付方式

        //courseOrderService.insert(courseOrder);


        //==================================================
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("购买课程[");

        //保存t_course_order_item
        courseInfos.stream().forEach(course2OrderMin -> {
            CourseOrderItem courseOrderItem = new CourseOrderItem();
            Course course = course2OrderMin.getCourse();
            boolean status = course.getStatus() == 1;
            AssertUtil.isTrue(status,"课程没有上线");
            courseOrderItem.setAmount(course2OrderMin.getCourseMarket().getPrice());//秒杀课程的价格
            courseOrderItem.setCount(1);//秒杀到课程的数量
            courseOrderItem.setCreateTime(date);//创建时间
            courseOrderItem.setCourseId(course.getId());
            courseOrderItem.setCourseName(course.getName());//秒杀课程名字
            courseOrderItem.setCoursePic(course.getPic());//封面
            courseOrderItem.setOrderNo(courseOrder.getOrderNo());
            courseOrder.setTotalCount(courseOrder.getTotalCount() + 1);
            stringBuilder.append(course.getName()).append("_");

            //courseOrderItem.setOrderId(courseOrder.getId());//对应秒杀订单
            //courseOrderItemService.insert(courseOrderItem);
            courseOrder.getCourseOrderItems().add(courseOrderItem);
        });
        stringBuilder.append("]");
        courseOrder.setTitle(stringBuilder.toString());
        courseOrderService.updateById(courseOrder);


        //发送保存支付单的事务消息
        Map<String, Object> map = new HashMap<>();
        map.put("loginId",loginId);
        map.put("courseId",courseId);
        Order2PayDto order2PayDto = new Order2PayDto(courseOrder.getTotalAmount(),
                coursePlaceOrderVo.getPayType(),
                courseOrder.getOrderNo(),
                courseOrder.getUserId(),
                JSONObject.toJSONString(map),
                courseOrder.getTitle());


        Message<String> order2PayDtoTmp = MessageBuilder.withPayload(JSONObject.toJSONString(order2PayDto)).build();
        //String txProducerGroup, String destination,
        // Message<?> message, Object arg

        TransactionSendResult sendResult = rocketMQTemplate.sendMessageInTransaction(
                "TxOrder2PayGroup",
                "order-topic:payorder-tags",
                order2PayDtoTmp,//检查本地事务参数
                courseOrder//Object arg执行本地事务参数
        );

        //执行本地事务的状态
        log.info("发送事务消息开始");
        LocalTransactionState localTransactionState = sendResult.getLocalTransactionState();
        //发送消息状态
        SendStatus sendStatus = sendResult.getSendStatus();
        log.info("发送事务消息完成");

        if( localTransactionState != LocalTransactionState.COMMIT_MESSAGE || sendStatus != SendStatus.SEND_OK ){
            throw new GlobalException("下单失败请重新下单");
        }


        //4.删除redis中的token
        redisTemplate.delete(loginId + ":" + courseId);

        //订单超时取消订单mq延迟消息 这里不能抛异常要自己try 否者上面事务消息无法回滚
        try {
            Message<String> message = MessageBuilder.withPayload(courseOrder.getOrderNo()).build();
            SendResult syncSend = rocketMQTemplate.syncSend(
                    "timeoutcancel-order-topic:paytimeout-tags",
                    message,
                    4000,
                    4
            );
            SendStatus sendStatus1 = syncSend.getSendStatus();
            if (sendStatus1 != SendStatus.SEND_OK){
                //发送消息失败
                //重试
                //将消息体保存到数据库，人工处理
            }
        } catch (Exception e) {
            e.printStackTrace();
            //发送消息失败
            //重试
            //将消息体保存到数据库，人工处理
        }
        //返回订单号调用支付
        return courseOrder.getOrderNo();
    }

    /**
     * 保存Order和Item
     * @param courseOrder
     */
    @Override
    public void savaOrderItem(CourseOrder courseOrder) {
        CourseOrder courseOrder1 =  selectByOrderNo(courseOrder.getOrderNo());
        AssertUtil.isNull(courseOrder1,"订单已经存在请勿重复下单");
        courseOrderService.insert(courseOrder);
        for (CourseOrderItem courseOrderItem : courseOrder.getCourseOrderItems()) {
            courseOrderItem.setOrderId(courseOrder.getId());
            courseOrderItemService.insert(courseOrderItem);
        }

    }

    /*查询orser*/
    @Override
    public CourseOrder selectByOrderNo(String orderNo) {
        EntityWrapper<CourseOrder> wrapper = new EntityWrapper<>();
        wrapper.eq("order_no",orderNo);
        return selectOne(wrapper);
    }

    /**
     * 修改支付状态业务判断
     * @param aliPayNotifyDto
     */
    @Override
    public void updateOrderPay(PayNotifyDto aliPayNotifyDto) {
        CourseOrder courseOrder = selectByOrderNo(aliPayNotifyDto.getOrderNo());
        if (courseOrder.getStatusOrder() == CourseOrder.STATE_UNPAID){
            courseOrder.setStatusOrder(CourseOrder.STATE_ACCOMPLISH);
            courseOrder.setUpdateTime(new Date());
            updateById(courseOrder);
        }else {
            //支付成功都是订单状态不对
            //支付成功 手动取消订单 执行退款逻辑
            //可以人工处理
        }
    }

    /**
     *  支付超时取消订单逻辑
     * @param orderNo
     */
    @Override
    public void payTimeOutCancelOrder(String orderNo) {
        CourseOrder courseOrder = selectByOrderNo(orderNo);
        //取消订单只有待支付才取消订单不是待支付不执行定时任务
        boolean b = courseOrder.getStatusOrder() == CourseOrder.STATE_UNPAID;
        if (!b){
            return;
        }
        courseOrder.setUpdateTime(new Date());
        courseOrder.setStatusOrder(CourseOrder.STATE_CANCEL);
        updateById(courseOrder);
    }

    /**
     * 秒杀下单返回订单号
     * @param coursePlaceOrderVo
     * @return
     */
    @Override
    public String killPlaceOrder(CoursePlaceOrderVo coursePlaceOrderVo) {
        //1.校验参数jsr303
        //2.防止重复token校验
        Long loginId = LoginSecurityContext.getLoginJwtDtoContext().getId();
        ForecastOrder forecastOrder = (ForecastOrder) redisTemplate.opsForValue().get(coursePlaceOrderVo.getOrderNo());
        Object token = redisTemplate.opsForValue().get(loginId + ":" + forecastOrder.getCourseId());
        AssertUtil.isNotNull(token,"请勿重复下单");
        boolean equals = coursePlaceOrderVo.getToken().equals(token.toString());
        AssertUtil.isTrue(equals,"token不正确");



        //==========================================
        Date date = new Date();
        //保存t_course_order表
        CourseOrder courseOrder = new CourseOrder();
        courseOrder.setCreateTime(date);//创建时间
        courseOrder.setOrderNo(forecastOrder.getOrderNo());//订单编号
        courseOrder.setTotalAmount(forecastOrder.getTotalAmount());//支付总的价格
        courseOrder.setStatusOrder(CourseOrder.STATE_UNPAID);//订单状态
        courseOrder.setUserId(loginId);//用户
        courseOrder.setPayType(coursePlaceOrderVo.getPayType());//支付方式
        courseOrder.setTotalCount(1);
        courseOrder.setTitle("课程[" + forecastOrder.getCourseName() +"]订单");

        //courseOrderService.insert(courseOrder);


        //==================================================
        StringBuilder stringBuilder = new StringBuilder();


        //保存t_course_order_item

        CourseOrderItem courseOrderItem = new CourseOrderItem();

        courseOrderItem.setAmount(forecastOrder.getTotalAmount());//秒杀课程的价格
        courseOrderItem.setCount(1);//秒杀到课程的数量
        courseOrderItem.setCreateTime(date);//创建时间
        courseOrderItem.setCourseId(forecastOrder.getCourseId());
        courseOrderItem.setCourseName(forecastOrder.getCourseName());//秒杀课程名字
        courseOrderItem.setCoursePic(forecastOrder.getCoursePic());//封面
        courseOrderItem.setOrderNo(forecastOrder.getOrderNo());


            //courseOrderItem.setOrderId(courseOrder.getId());//对应秒杀订单
            //courseOrderItemService.insert(courseOrderItem);
        courseOrder.getCourseOrderItems().add(courseOrderItem);





        //发送保存支付单的事务消息
        Map<String, Object> map = new HashMap<>();
        map.put("loginId",loginId);
        map.put("courseId",forecastOrder.getCourseId());
        Order2PayDto order2PayDto = new Order2PayDto(courseOrder.getTotalAmount(),
                coursePlaceOrderVo.getPayType(),
                courseOrder.getOrderNo(),
                courseOrder.getUserId(),
                JSONObject.toJSONString(map),
                courseOrder.getTitle());


        Message<String> order2PayDtoTmp = MessageBuilder.withPayload(JSONObject.toJSONString(order2PayDto)).build();
        //String txProducerGroup, String destination,
        // Message<?> message, Object arg

        TransactionSendResult sendResult = rocketMQTemplate.sendMessageInTransaction(
                "TxOrder2PayGroup",
                "order-topic:payorder-tags",
                order2PayDtoTmp,//检查本地事务参数
                courseOrder//Object arg执行本地事务参数
        );

        //执行本地事务的状态
        log.info("发送事务消息开始");
        LocalTransactionState localTransactionState = sendResult.getLocalTransactionState();
        //发送消息状态
        SendStatus sendStatus = sendResult.getSendStatus();
        log.info("发送事务消息完成");

        if( localTransactionState != LocalTransactionState.COMMIT_MESSAGE || sendStatus != SendStatus.SEND_OK ){
            throw new GlobalException("下单失败请重新下单");
        }


        //4.删除redis中的token
        redisTemplate.delete(loginId + ":" + forecastOrder.getCourseId());
        //删除预创订单
        redisTemplate.delete(forecastOrder.getOrderNo());

        //订单超时取消订单mq延迟消息 这里不能抛异常要自己try 否者上面事务消息无法回滚
        try {
            Message<String> message = MessageBuilder.withPayload(courseOrder.getOrderNo()).build();
            SendResult syncSend = rocketMQTemplate.syncSend(
                    "timeoutcancel-order-topic:paytimeout-tags",
                    message,
                    4000,
                    6
            );
            SendStatus sendStatus1 = syncSend.getSendStatus();
            if (sendStatus1 != SendStatus.SEND_OK){
                //发送消息失败
                //重试
                //将消息体保存到数据库，人工处理
            }
        } catch (Exception e) {
            e.printStackTrace();
            //发送消息失败
            //重试
            //将消息体保存到数据库，人工处理
        }
        //返回订单号调用支付
        return courseOrder.getOrderNo();
    }
}
